/**
 * Copyright (c) 2009-2011, chunquedong(YangJiandong)
 * 
 * This file is part of ChunMap project
 * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE(Version >=3)
 * 
 * History:
 *     2010-05-05  Jed Young  Creation
 */
package chunmap.model.algorithm;

import java.util.ArrayList;
import java.util.List;

import chunmap.model.coord.CompactedCoordinateArray;
import chunmap.model.coord.CoordinateSeq;
import chunmap.model.coord.Position;
import chunmap.model.elem.LineSegment;

public class LinearReference {
	/// <summary>
    /// 指定距离处的点
    /// </summary>
    /// <param name="points"></param>
    /// <param name="distance"></param>
    /// <returns></returns>
    public static Position refPoint(CoordinateSeq points, double distance)
    {
        checkArgument(points, distance);
        double len = 0;
        int i = 0;
        LineSegment lseg = null;
        for (int n = points.size() - 1; i < n; i++)
        {
            lseg = new LineSegment(points.getCoordinate(i), points.getCoordinate(i+1));
            len += lseg.getDistance();
            if (len > distance)
            {
                break;
            }
        }
        double l2 = len - distance;
        double d = lseg.getDistance();
        double l1 = d - l2;
        double lamuda = l1 / l2;
        return lseg.dingBiFenDian(lamuda);
    }

    /// <summary>
    /// 子线串
    /// </summary>
    /// <param name="points"></param>
    /// <param name="start"></param>
    /// <param name="end"></param>
    /// <returns></returns>
    public static CoordinateSeq subLineString(CoordinateSeq points, double start, double end)
    {
        checkArgument(points, start);
        checkArgument(points, end);
        Position sp = refPoint(points, start);
        Position ep = refPoint(points, end);
        if (start < end)
        {
            return middleLineString(points, sp, ep);
        }
        else if (start > end)
        {
            return middleLineString(points, ep, sp);
        }
        else
        {
            return null;
        }
    }


    //------------------------------------------------------------------------helper

    // 两点中间的线
    private static CoordinateSeq middleLineString(CoordinateSeq points, Position start, Position end)
    {
        List<CoordinateSeq> ml = splitLine(points, start);
        if (ml.size() == 0)
        {
            throw new IllegalArgumentException("IllegalArgument exist");
        }
        int i = (ml.size() == 1) ? 0 : 1;
        CoordinateSeq tl = ml.get(i);
        List<CoordinateSeq> ml2 = splitLine(tl, end);
        return ml2.get(0);
    }

    public static List<CoordinateSeq> splitLine(CoordinateSeq points, Position p)
    {
        CoordinateSeq lineString = (CoordinateSeq)points.clone();
        if (!LineAlgorithm.onLineString(points, p))
        {
            return new ArrayList<CoordinateSeq>();
        }
        if (onBorder(lineString, p))
        {
            List<CoordinateSeq> lines = new ArrayList<CoordinateSeq>();
            lines.add(points);
            return lines;
        }
        // 分裂
        List<Position> points1 = new ArrayList<Position>();
        List<Position> points2 = new ArrayList<Position>();
        for (int i = 0, n = points.size() - 1; i < n; i++)
        {
            LineSegment lseg = new LineSegment(points.getCoordinate(i), points.getCoordinate(i+1));
            if (lseg.onLineSegment(p))
            {
                List<Position> tpoints = subList(points, 0, i + 1);
                points1.addAll(tpoints);
                points1.add(p);

                if (!lseg.onBorder(p))
                {
                    points2.add(p);
                }
                List<Position> tpoints2 = subList(points, i + 1, n + 1);
                points2.addAll(tpoints2);
                break;
            }
        }
        // 构造多线
        CoordinateSeq l1 = new CompactedCoordinateArray(points1.toArray(new Position[0]));
        CoordinateSeq l2 = new CompactedCoordinateArray(points2.toArray(new Position[0]));
        List<CoordinateSeq> lines2 = new ArrayList<CoordinateSeq>();
        lines2.add(l1);
        lines2.add(l2);
        return lines2;
        //return null;
    }
    private static boolean onBorder(CoordinateSeq points, Position p)
    {
        if (p.equals(points.startPoint()) || p.equals(points.endPoint()))
            return true;
        return false;
    }
    private static List<Position> subList(CoordinateSeq points, int start, int end)
    {
        List<Position> sub = new ArrayList<Position>();
        for (int i = start; i < end; i++)
        {
            sub.add(points.getCoordinate(i));
        }
        return sub;
    }

    private static void checkArgument(CoordinateSeq points, double distance)
    {
        if (distance < 0 || distance > getLength(points))
        {
            throw new IllegalArgumentException();
        }
    }
    public static double getLength(CoordinateSeq points)
    {
        double len = 0;
        for (int i = 0, n = points.size() - 1; i < n; i++)
        {
            len += points.getCoordinate(i).distance2D(points.getCoordinate(i+1));
        }
        return len;
    }
}